package tesis.common.security.filter;

import tesis.common.security.CustomUserDetailsService;
import tesis.common.security.SeguridadService;
import tesis.domain.security.UsuarioSeguridad;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.TextEscapeUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

/**
 *
 * @author diego.gil
 */
public class CustomAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    private static Logger log = LoggerFactory.getLogger(CustomAuthenticationFilter.class);
    private String superusernameParameter = "j_superusername";
    private String usernameParameter = "j_username";
    private String passwordParameter = "j_password";
    private String rolParameter = "rol";
    private static final String DEFAULT_FILTER_PROCESSES_URL = "/j_spring_security_check";
    private static final String POST = "POST";
    private static final String GET = "GET";
    private boolean postOnly = false;
    private CustomUserDetailsService customUserDetailsService;
    private WebApplicationContext wac;

    public CustomAuthenticationFilter() {
        super(DEFAULT_FILTER_PROCESSES_URL);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        log.debug("<<-------------------------- CustomAuthenticationFilter - attemptAuthentication");
        log.debug("postOnly [{}] request.getMethod [{}]", postOnly, request.getMethod());

        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }
        String superusername = obtainSuperUsername(request);
        String username = obtainUsername(request);
        String password = obtainPassword(request);
        String rol = obtainRol(request);
        log.debug("username [{}] password [{}] rol [{}]", new Object[]{username, password, rol});
        if (username == null) {
            username = "";
        }
        if (password == null) {
            password = "";
        }
        rol = (rol == null) ? "" : rol;
        username = username.trim();
        wac = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
        CustomUserDetailsService sus = (CustomUserDetailsService) wac.getBean("customUserDetailsService");
        SeguridadService seguridadService = (SeguridadService) wac.getBean("seguridadService");
        UsuarioSeguridad u = sus.getUsuarioSeguridad(username);
        password = seguridadService.desencriptar(u.getUsuario(), u.getClave());

        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
        // Place the last username attempted into HttpSession for views
        HttpSession session = request.getSession(false);
        if (session != null || getAllowSessionCreation()) {

            request.getSession().setAttribute(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY, TextEscapeUtils.escapeEntities(username));
        }
        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);

        return getAuthenticationManager().authenticate(authRequest);
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        log.debug("<<-------------------------- CustomAuthenticationFilter - doFilter");
        final HttpServletRequest request = (HttpServletRequest) req;
        request.setCharacterEncoding("UTF-8");
        final HttpServletResponse response = (HttpServletResponse) res;
        String uri = request.getRequestURI();
        log.debug("postOnly [{}] request.getMethod [{}] URI [{}] URL [{}]", new Object[]{postOnly, request.getMethod(), uri, request.getRequestURL().toString()});
        if (postOnly) {
            if (request.getMethod().equals(POST)) {
                // If the incoming request is a POST, then we send it up
                // to the AbstractAuthenticationProcessingFilter.
                super.doFilter(request, response, chain);
            } else {
                // If it's a GET, we ignore this request and send it
                // to the next filter in the chain.  In this case, that
                // pretty much means the request will hit the /login
                // controller which will process the request to show the
                // login page.
                chain.doFilter(request, response);
            }
        } else {
            super.doFilter(request, response, chain);
        }
    }

    /**
     * Enables subclasses to override the composition of the password, such as
     * by including additional values and a separator.<p>This might be used for
     * example if a postcode/zipcode was required in addition to the password. A
     * delimiter such as a pipe (|) should be used to separate the password and
     * extended value(s). The
     * <code>AuthenticationDao</code> will need to generate the expected
     * password in a corresponding manner.</p>
     *
     * @param request so that request attributes can be retrieved
     *
     * @return the password that will be presented in the
     * <code>Authentication</code> request token to the
     * <code>AuthenticationManager</code>
     */
    protected String obtainPassword(HttpServletRequest request) {
        return request.getParameter(passwordParameter);
    }

    /**
     * Enables subclasses to override the composition of the username, such as
     * by including additional values and a separator.
     *
     * @param request so that request attributes can be retrieved
     *
     * @return the username that will be presented in the
     * <code>Authentication</code> request token to the
     * <code>AuthenticationManager</code>
     */
    protected String obtainUsername(HttpServletRequest request) {
        return request.getParameter(usernameParameter);
    }

    protected String obtainRol(HttpServletRequest request) {
        return request.getParameter(rolParameter);
    }

    protected String obtainSuperUsername(HttpServletRequest request) {
        return request.getParameter(superusernameParameter);
    }

    /**
     * Provided so that subclasses may configure what is put into the
     * authentication request's details property.
     *
     * @param request that an authentication request is being created for
     * @param authRequest the authentication request object that should have its
     * details set
     */
    protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }

    public String getPasswordParameter() {
        return passwordParameter;
    }

    public void setPasswordParameter(String passwordParameter) {
        this.passwordParameter = passwordParameter;
    }

    public String getUsernameParameter() {
        return usernameParameter;
    }

    public void setUsernameParameter(String usernameParameter) {
        this.usernameParameter = usernameParameter;
    }

    public boolean isPostOnly() {
        return postOnly;
    }

    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }

    public String getRolParameter() {
        return rolParameter;
    }

    public void setRolParameter(String rolParameter) {
        this.rolParameter = rolParameter;
    }

    public String getSuperusernameParameter() {
        return superusernameParameter;
    }

    public void setSuperusernameParameter(String superusernameParameter) {
        this.superusernameParameter = superusernameParameter;
    }
}
